package com.black.utils.compare;

import java.util.Comparator;
import java.util.function.Function;

/**
 * @author ylx
 */
public class CompareUtil {

    /**
     * 比较两个可比较对象的大小。
     * <p>
     * 此方法提供了一种通用的方式来比较两种类型的对象，只要这两种类型实现了Comparable接口。
     * 通过使用泛型<T extends Comparable<T>>，此方法可以适用于任何实现了Comparable接口的类型T。
     * 这种设计使得代码更加通用和灵活，避免了因为类型不匹配而导致的编译错误。
     *
     * @param x 第一个要比较的对象，必须实现Comparable接口。
     * @param y 第二个要比较的对象，必须实现Comparable接口。
     * @return 返回比较的结果。如果x小于y，则返回负数；如果x等于y，则返回0；如果x大于y，则返回正数。
     * @throws IllegalArgumentException 如果x或y为null，或者它们无法相互比较（即实现了Comparable接口，但两个对象的类型不兼容），则抛出此异常。
     */
    public static <T extends Comparable<T>> int compare(T x, T y) throws IllegalArgumentException {
        return x.compareTo(y);
    }

    // 大于
    public static <T extends Comparable<T>> boolean gt(T x, T y) {
        return compare(x, y) > 0;
    }

    // 小于
    public static <T extends Comparable<T>> boolean lt(T x, T y) {
        return compare(x, y) < 0;
    }

    // 等于
    public static <T extends Comparable<T>> boolean eq(T x, T y) {
        return compare(x, y) == 0;
    }

    // 大于等于
    public static <T extends Comparable<T>> boolean gte(T x, T y) {
        return compare(x, y) >= 0;
    }

    // 小于等于
    public static <T extends Comparable<T>> boolean lte(T x, T y) {
        return compare(x, y) <= 0;
    }

    /**
     * 使用多个比较器比较两个对象。
     * <p>
     * 此方法允许通过可变参数传递一个或多个比较器，用于逐个比较两个对象。比较过程从第一个比较器开始，
     * 如果两个对象在当前比较器下的比较结果不相等，则返回该比较结果；如果所有比较器都认为两个对象相等，
     * 则返回0。这种方式为比较对象提供了更大的灵活性，可以根据不同需求组合不同的比较策略。
     *
     * @param x           要比较的第一个对象。
     * @param y           要比较的第二个对象。
     * @param comparators 一个或多个比较器，用于对对象进行比较。
     * @param <T>         比较对象的类型，必须与比较器的泛型参数一致。
     * @return 返回比较结果，如果x和y在所有比较器下都相等，则返回0；否则返回第一个不为0的比较结果。
     */
    @SafeVarargs
    public static <T> int compare(T x, T y, Comparator<T>... comparators) {
        // 遍历所有比较器  (不构造链式 .thenCompare)
        for (Comparator<T> comparator : comparators) {
            // 使用当前比较器比较两个对象
            int result = comparator.compare(x, y);
            // 如果比较结果不为0，说明两个对象在当前比较器下不相等，直接返回比较结果
            if (result != 0) {
                return result;
            }
        }
        // 如果所有比较器都认为两个对象相等，返回0
        return 0;
    }

    /**
     * 使用多个比较器比较两个对象。
     * <p>
     * 此方法允许通过可变参数传递一个或多个比较器，用于逐个比较两个对象。比较过程从第一个比较器开始，
     * 如果两个对象在当前比较器下的比较结果不相等，则返回该比较结果；如果所有比较器都认为两个对象相等，
     * 则返回0。这种方式为比较对象提供了更大的灵活性，可以根据不同需求组合不同的比较策略。
     *
     * @param x           要比较的第一个对象。
     * @param y           要比较的第二个对象。
     * @param comparators 一个或多个比较器，用于对对象进行比较。
     * @param <T>         比较对象的类型，必须与比较器的泛型参数一致。
     * @return 返回比较结果，如果x和y在所有比较器下都相等，则返回0；否则返回第一个不为0的比较结果。
     */
    @SafeVarargs
    public static <T> int chainCompare(T x, T y, Comparator<T>... comparators) {
        return buildComparator(comparators).compare(x, y);
    }

    @SafeVarargs
    public static <T> Comparator<T> buildComparator(Comparator<T>... comparators) {
        // 构造链式比较器
        Comparator<T> chain = comparators[0];
        for (int i = 1; i < comparators.length; i++) {
            chain = chain.thenComparing(comparators[i]);
        }
        return chain;
    }

    // 大于
    @SafeVarargs
    public static <T> boolean gt(T x, T y, Comparator<T>... comparators) {
        return compare(x, y, comparators) > 0;
    }

    // 小于
    @SafeVarargs
    public static <T> boolean lt(T x, T y, Comparator<T>... comparators) {
        return compare(x, y, comparators) < 0;
    }

    // 等于
    @SafeVarargs
    public static <T> boolean eq(T x, T y, Comparator<T>... comparators) {
        return compare(x, y, comparators) == 0;
    }

    // 大于等于
    @SafeVarargs
    public static <T> boolean gte(T x, T y, Comparator<T>... comparators) {
        return compare(x, y, comparators) >= 0;
    }

    // 小于等于
    @SafeVarargs
    public static <T> boolean lte(T x, T y, Comparator<T>... comparators) {
        return compare(x, y, comparators) <= 0;
    }

    /**
     * 根据多个 getter 函数获取的值，比较两个对象的大小以确定它们的顺序。
     * 此方法允许你根据多个属性来比较同类型对象的大小。
     *
     * @param <T>          对象类型泛型
     * @param <R>          比较属性的类型，需实现 Comparable 接口
     * @param x            第一个待比较的对象
     * @param y            第二个待比较的对象
     * @param fieldGetters 变长函数数组，用于从对象中获取比较所需的属性值
     * @return 如果 x 小于 y，返回负数；如果 x 大于 y，返回正数；如果两者相等，返回 0。
     * 如果在比较过程中遇到 null 值，将抛出 NullPointerException。
     */
    @SafeVarargs
    public static <T, R extends Comparable<R>> int compare(T x, T y, Function<T, R>... fieldGetters) {
        // 遍历所有用于获取比较属性的函数
        for (Function<T, R> fieldGetter : fieldGetters) {
            R xValue = fieldGetter.apply(x);
            R yValue = fieldGetter.apply(y);

            // 检查获取的属性值是否为 null，若为 null 则抛出异常
            if (xValue == null || yValue == null) {
                throw new NullPointerException("xValue 或 yValue 为 null");
            } else {
                // 使用Comparable接口的compareTo方法比较属性值
                int result = xValue.compareTo(yValue);

                // 若比较结果不为0，即找到决定性属性，返回比较结果
                if (result != 0) {
                    return result;
                }
            }
        }
        // 所有属性都比较后仍无法决定大小关系，则认为两者相等
        return 0;
    }

    @SafeVarargs
    public static <T, R extends Comparable<R>> boolean gt(T x, T y, Function<T, R>... fieldGetters) {
        return compare(x, y, fieldGetters) > 0;
    }

    @SafeVarargs
    public static <T, R extends Comparable<R>> boolean lt(T x, T y, Function<T, R>... fieldGetters) {
        return compare(x, y, fieldGetters) < 0;
    }

    @SafeVarargs
    public static <T, R extends Comparable<R>> boolean eq(T x, T y, Function<T, R>... fieldGetters) {
        return compare(x, y, fieldGetters) == 0;
    }

    @SafeVarargs
    public static <T, R extends Comparable<R>> boolean gte(T x, T y, Function<T, R>... fieldGetters) {
        return compare(x, y, fieldGetters) >= 0;
    }

    @SafeVarargs
    public static <T, R extends Comparable<R>> boolean lte(T x, T y, Function<T, R>... fieldGetters) {
        return compare(x, y, fieldGetters) <= 0;
    }
}
